package com.itaming.lycheeframework.support.lambda;

import com.itaming.lycheeframework.support.utils.ClassUtil;
import com.itaming.lycheeframework.support.utils.ReflectionUtil;
import com.itaming.lycheeframework.support.utils.StringPool;
import com.itaming.lycheeframework.support.utils.StringUtil;
import org.springframework.util.ClassUtils;

import java.beans.Introspector;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 可序列化的 Lambda 表达式
 *
 * @author A.Ming
 */
public interface SerializableLambda<R> extends Serializable {

    /**
     * 返回值类型匹配模式
     */
    Pattern RETURN_TYPE_PATTERN = Pattern.compile("\\(.*\\)L(.*);");

    /**
     * 参数类型匹配模式
     */
    Pattern PARAMETER_TYPE_PATTERN = Pattern.compile("\\((.*)\\).*");

    /**
     * 获取 {@code SerializedLambda}
     * <p>
     * Java 虚拟机自动给实现 {@link Serializable} 接口的 Lambda 表达式
     * 生成 {@code writeReplace()} 方法，该方法返回一个 {@link SerializedLambda} 对象，
     * 其中包含了 Lambda 表达式的所有信息
     *
     * @return SerializedLambda
     */
    default SerializedLambda getSerializedLambda() {
        try {
            return (SerializedLambda) ReflectionUtil.invokeMethod(getClass(), "writeReplace", this);
        } catch (Exception e) {
            throw new IllegalStateException("Not a serializable lambda: " + this, e);
        }
    }

    /**
     * 获取返回值类型
     *
     * @return 返回值类型
     */
    @SuppressWarnings("unchecked")
    default Class<R> getReturnType() {
        String instantiatedMethodType = getSerializedLambda().getInstantiatedMethodType();
        Matcher matcher = RETURN_TYPE_PATTERN.matcher(instantiatedMethodType);
        if (!matcher.find() || matcher.groupCount() != 1) {
            throw new IllegalStateException("Could not match return type");
        }

        return (Class<R>) ClassUtil.resolveClassName(matcher.group(1).replace(StringPool.SLASH, StringPool.DOT), null);
    }

    /**
     * 获取参数类型
     *
     * @return 参数类型列表
     */
    default List<Class<?>> getParameterTypes() {
        String instantiatedMethodType = getSerializedLambda().getInstantiatedMethodType();
        Matcher matcher = PARAMETER_TYPE_PATTERN.matcher(instantiatedMethodType);
        if (!matcher.find() || matcher.groupCount() != 1) {
            throw new IllegalStateException("Could not match parameter type");
        }

        String[] classDescriptionArray = matcher.group(1).split(StringPool.SEMICOLON);
        List<Class<?>> result = new ArrayList<>(classDescriptionArray.length);
        for (String classDescription : classDescriptionArray) {
            if (StringUtil.isNotBlank(classDescription)) {
                String className = classDescription.replace("L", StringPool.EMPTY).replace(StringPool.SLASH, StringPool.DOT);
                result.add(ClassUtil.resolveClassName(className, null));
            }
        }

        return result;
    }

    /**
     * 获取实现类
     *
     * @return 实现类
     */
    default Class<?> getImplClass() {
        String implClass = getSerializedLambda().getImplClass();
        return ClassUtils.resolveClassName(implClass.replace(StringPool.SLASH, StringPool.DOT), null);
    }

    /**
     * 通过 {@code getter} 方法获取属性名，不支持 {@code boolean} 类型的 {@code isXXX} 方法
     *
     * @return 属性名
     */
    default String getPropertyName() {
        String implMethodName = getSerializedLambda().getImplMethodName();
        if (!StringUtil.startsWith(implMethodName, "get") || implMethodName.length() <= 3) {
            throw new IllegalArgumentException("Not a getter: " + this);
        }

        return Introspector.decapitalize(implMethodName.substring(3));
    }

}
